<script lang="ts">
	import { type FlowModule } from '$lib/gen'
	import { getContext } from 'svelte'

	import type { FlowEditorContext } from '../types'
	import FlowLoop from './FlowLoop.svelte'
	import FlowModuleComponent from './FlowModuleComponent.svelte'
	import FlowBranchAllWrapper from './FlowBranchAllWrapper.svelte'
	import FlowBranchOneWrapper from './FlowBranchOneWrapper.svelte'
	import {
		createInlineScriptModule,
		pickFlow,
		pickScript
	} from '$lib/components/flows/flowStateUtils'
	import FlowInputs from './FlowInputs.svelte'
	import { Alert } from '$lib/components/common'
	import FlowInputsFlow from './FlowInputsFlow.svelte'
	import FlowBranchesAllWrapper from './FlowBranchesAllWrapper.svelte'
	import FlowBranchesOneWrapper from './FlowBranchesOneWrapper.svelte'
	import FlowWhileLoop from './FlowWhileLoop.svelte'
	import { initFlowStepWarnings } from '../utils'
	import { dfs } from '../dfs'
	import type { TriggerContext } from '$lib/components/triggers'

	export let flowModule: FlowModule
	export let noEditor: boolean = false
	export let enableAi = false

	const { selectedId, flowStateStore, flowInputsStore, flowStore } =
		getContext<FlowEditorContext>('FlowEditorContext')

	const { primarySchedule } = getContext<TriggerContext>('TriggerContext')

	let scriptKind: 'script' | 'trigger' | 'approval' = 'script'
	let scriptTemplate: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' = 'script'

	// These pointers are used to easily access previewArgs of parent module, and previous module
	// Pointer to parent module, only defined within Branches or Loops.
	export let parentModule: FlowModule | undefined = undefined
	// Pointer to previous module, for easy access to testing results
	export let previousModule: FlowModule | undefined = undefined

	function initializePrimaryScheduleForTriggerScript(module: FlowModule) {
		if (!$primarySchedule) {
			$primarySchedule = {
				summary: 'Scheduled poll of flow',
				args: {},
				cron: '0 */15 * * *',
				timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
				enabled: true
			}
		}
		if (!$primarySchedule.cron) {
			$primarySchedule.cron = '0 */15 * * *'
		}
		$primarySchedule.enabled = true

		module.stop_after_if = {
			expr: 'result == undefined || Array.isArray(result) && result.length == 0',
			skip_if_stopped: true
		}
	}
	async function createModuleFromScript(
		path: string,
		summary: string,
		kind: string,
		hash: string | undefined
	) {
		const [module, state] = await pickScript(path, summary, flowModule.id, hash)

		if (kind == 'approval') {
			module.suspend = { required_events: 1, timeout: 1800 }
		}

		if (kind == 'trigger') {
			initializePrimaryScheduleForTriggerScript(module)
		}

		flowModule = module
		$flowStateStore[module.id] = state

		if ($flowInputsStore) {
			$flowInputsStore[module?.id] = {
				flowStepWarnings: await initFlowStepWarnings(
					module?.value,
					$flowStateStore[module?.id]?.schema,
					dfs($flowStore.value.modules, (fm) => fm.id)
				)
			}
		}
	}
</script>

{#if flowModule.id === $selectedId}
	{#if flowModule.value.type === 'forloopflow'}
		<FlowLoop {noEditor} bind:mod={flowModule} {parentModule} {previousModule} {enableAi} />
	{:else if flowModule.value.type === 'whileloopflow'}
		<FlowWhileLoop {noEditor} bind:mod={flowModule} {previousModule} {parentModule} />
	{:else if flowModule.value.type === 'branchone'}
		<FlowBranchesOneWrapper {noEditor} {previousModule} {parentModule} bind:flowModule {enableAi} />
	{:else if flowModule.value.type === 'branchall'}
		<FlowBranchesAllWrapper {noEditor} {previousModule} {parentModule} bind:flowModule />
	{:else if flowModule.value.type === 'identity'}
		{#if $selectedId == 'failure'}
			<div class="p-4">
				<Alert type="info" title="Error handlers are triggered upon non recovered errors">
					If defined, the error handler will take the error as input.
				</Alert>
			</div>
		{:else if $selectedId == 'preprocessor'}
			<div class="p-4">
				<Alert
					type="info"
					title="Preprocessor is called when the flow is triggered by API or email"
				>
					It prepares arguments for the flow. Besides request arguments, the preprocessor receives a
					`wm_trigger` argument with trigger details.
				</Alert>
			</div>
		{/if}

		{#if flowModule.value.flow}
			<FlowInputsFlow
				on:pick={async ({ detail }) => {
					const { path, summary } = detail
					const [module, state] = await pickFlow(path, summary, flowModule.id)

					flowModule = module
					$flowStateStore[module.id] = state
				}}
			/>
		{:else}
			<FlowInputs
				{noEditor}
				summary={flowModule.summary}
				shouldDisableTriggerScripts={parentModule !== undefined ||
					previousModule !== undefined ||
					$selectedId == 'failure' ||
					$selectedId == 'preprocessor'}
				on:pick={async ({ detail }) => {
					const { path, summary, kind, hash } = detail
					createModuleFromScript(path, summary, kind, hash)
				}}
				on:new={async ({ detail }) => {
					const { language, kind, subkind, summary } = detail

					const [module, state] = await createInlineScriptModule(
						language,
						kind,
						subkind,
						flowModule.id,
						summary
					)
					scriptKind = kind
					scriptTemplate = subkind

					if (kind == 'trigger') {
						initializePrimaryScheduleForTriggerScript(module)
					}

					if (kind == 'approval') {
						module.suspend = { required_events: 1, timeout: 1800 }
					}

					flowModule = module
					$flowStateStore[module.id] = state

					if ($flowInputsStore) {
						$flowInputsStore[module.id] = {
							flowStepWarnings: await initFlowStepWarnings(
								module.value,
								$flowStateStore[module.id].schema,
								dfs($flowStore.value.modules, (fm) => fm.id)
							)
						}
					}
				}}
				failureModule={$selectedId === 'failure'}
				preprocessorModule={$selectedId === 'preprocessor'}
			/>
		{/if}
	{:else if flowModule.value.type === 'rawscript' || flowModule.value.type === 'script' || flowModule.value.type === 'flow'}
		<FlowModuleComponent
			{noEditor}
			bind:flowModule
			{parentModule}
			{previousModule}
			failureModule={$selectedId === 'failure'}
			preprocessorModule={$selectedId === 'preprocessor'}
			{scriptKind}
			{scriptTemplate}
			{enableAi}
		/>
	{/if}
{:else if flowModule.value.type === 'forloopflow' || flowModule.value.type == 'whileloopflow'}
	{#each flowModule.value.modules as submodule, index (index)}
		<svelte:self
			{noEditor}
			bind:flowModule={submodule}
			bind:parentModule={flowModule}
			previousModule={flowModule.value.modules[index - 1]}
			{enableAi}
		/>
	{/each}
{:else if flowModule.value.type === 'branchone'}
	{#if $selectedId === `${flowModule?.id}-branch-default`}
		<div class="p-2">
			<h3 class="mb-4">Default branch</h3>
			Nothing to configure, this is the default branch if none of the predicates are met.
		</div>
	{:else}
		{#each flowModule.value.default as submodule, index}
			<svelte:self
				{noEditor}
				bind:flowModule={submodule}
				bind:parentModule={flowModule}
				previousModule={flowModule.value.default[index - 1]}
				{enableAi}
			/>
		{/each}
	{/if}
	{#each flowModule.value.branches as branch, branchIndex (branchIndex)}
		{#if $selectedId === `${flowModule?.id}-branch-${branchIndex}`}
			<FlowBranchOneWrapper
				{noEditor}
				bind:branch
				parentModule={flowModule}
				{previousModule}
				{enableAi}
			/>
		{:else}
			{#each branch.modules as submodule, index}
				<svelte:self
					{noEditor}
					bind:flowModule={submodule}
					bind:parentModule={flowModule}
					previousModule={flowModule.value.branches[branchIndex].modules[index - 1]}
					{enableAi}
				/>
			{/each}
		{/if}
	{/each}
{:else if flowModule.value.type === 'branchall'}
	{#each flowModule.value.branches as branch, branchIndex (branchIndex)}
		{#if $selectedId === `${flowModule?.id}-branch-${branchIndex}`}
			<FlowBranchAllWrapper {noEditor} bind:branch />
		{:else}
			{#each branch.modules as submodule, index}
				<svelte:self
					{noEditor}
					bind:flowModule={submodule}
					bind:parentModule={flowModule}
					previousModule={flowModule.value.branches[branchIndex].modules[index - 1]}
					{enableAi}
				/>
			{/each}
		{/if}
	{/each}
{/if}
